package com.yuanfengPlus.production.service.impl;

import com.yuanfengPlus.production.Mapper.*;
import com.yuanfengPlus.production.model.ProdMachine;
import com.yuanfengPlus.production.model.ProdProductionOrders;
import com.yuanfengPlus.production.model.ProdProductionPlans;
import com.yuanfengPlus.production.model.ProdWorkCenter;
import com.yuanfengPlus.production.service.SchedulingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SchedulingServiceImpl implements SchedulingService {

    @Autowired
    private ProdProductionOrdersMapper productionOrdersMapper; // 注入生产订单映射器

    @Autowired
    private ProdWorkCentersMapper workCentersMapper; // 注入工作中心映射器

    @Autowired
    private ProdMachinesMapper machinesMapper; // 注入设备映射器

    @Autowired
    private ProdEmployeesMapper employeesMapper; // 注入员工映射器

    @Autowired
    private ProdProductionPlansMapper productionPlansMapper; // 注入生产计划映射器，用于保存生成的生产计划

    private static final double INITIAL_TEMPERATURE = 1000; // 初始温度
    private static final double FINAL_TEMPERATURE = 1; // 最终温度
    private static final double ALPHA = 0.995; // 温度衰减系数
    @Override
    // 生成生产计划并优化
    public ProdProductionPlans generateAndOptimizeProductionPlan(int planId) {
        List<ProdProductionOrders> orders = productionOrdersMapper.selectByPlanId(planId); // 获取指定计划ID的所有生产订单
        List<ProdWorkCenter> workCenters = workCentersMapper.selectAll(); // 获取所有工作中心
        List<ProdMachine> machines = machinesMapper.selectAll(); // 获取所有设备

        // 生成初始的生产计划
        SchedulingSolution currentSolution = generateInitialSolution(orders, workCenters, machines);
        double currentEnergy = calculateEnergy(currentSolution); // 计算初始解的能量

        double temperature = INITIAL_TEMPERATURE; // 设置初始温度
        SchedulingSolution bestSolution = currentSolution; // 保存最佳解
        double bestEnergy = currentEnergy; // 保存最佳解的能量

        // 模拟退火过程，优化生产计划
        while (temperature > FINAL_TEMPERATURE) {
            SchedulingSolution newSolution = generateNeighborSolution(currentSolution); // 生成邻域解
            double newEnergy = calculateEnergy(newSolution); // 计算邻域解的能量

            if (newEnergy < currentEnergy || acceptSolution(currentEnergy, newEnergy, temperature)) {
                currentSolution = newSolution; // 更新当前解
                currentEnergy = newEnergy; // 更新当前能量

                if (currentEnergy < bestEnergy) {
                    bestSolution = currentSolution; // 更新最佳解
                    bestEnergy = currentEnergy; // 更新最佳能量
                }
            }

            temperature *= ALPHA; // 降低温度
        }

        // 将优化后的生产计划保存到数据库
        return saveOptimizedProductionPlan(bestSolution);
    }

    // 生成初始解
    private SchedulingSolution generateInitialSolution(List<ProdProductionOrders> orders, List<ProdWorkCenter> workCenters, List<ProdMachine> machines) {
        Random rand = new Random(); // 创建随机数生成器
        Map<ProdProductionOrders, Assignment> scheduleAssignments = new HashMap<>(); // 创建生产订单与分配的映射
        for (ProdProductionOrders order : orders) {
            ProdWorkCenter workCenter = workCenters.get(rand.nextInt(workCenters.size())); // 随机选择一个工作中心
            ProdMachine machine = machines.stream()
                    .filter(m -> m.getWorkCenterId().equals(workCenter.getWorkCenterId())) // 过滤出属于该工作中心的设备
                    .skip(rand.nextInt((int) machines.stream().filter(m -> m.getWorkCenterId().equals(workCenter.getWorkCenterId())).count())) // 随机选择一个设备
                    .findFirst().orElse(null);
            scheduleAssignments.put(order, new Assignment(workCenter, machine)); // 为每个订单分配工作中心和设备
        }
        return new SchedulingSolution(scheduleAssignments);
    }

    // 计算解的能量（生产时间）
    private double calculateEnergy(SchedulingSolution solution) {
        double totalTime = 0.0; // 初始化总时间
        for (Map.Entry<ProdProductionOrders, Assignment> entry : solution.getAssignments().entrySet()) {
            ProdProductionOrders order = entry.getKey(); // 获取生产订单
            Assignment assignment = entry.getValue(); // 获取分配
            double orderTime = order.getQuantity() * 1.0; // 计算每个订单的生产时间
            totalTime += orderTime; // 累加总时间
        }
        return totalTime; // 返回总能量
    }

    // 生成邻域解
    private SchedulingSolution generateNeighborSolution(SchedulingSolution currentSolution) {
        SchedulingSolution newSolution = currentSolution.copy(); // 复制当前解
        Random rand = new Random(); // 创建随机数生成器

        List<ProdProductionOrders> orders = new ArrayList<>(currentSolution.getAssignments().keySet()); // 获取所有生产订单
        ProdProductionOrders selectedOrder = orders.get(rand.nextInt(orders.size())); // 随机选择一个订单
        Assignment currentAssignment = currentSolution.getAssignments().get(selectedOrder); // 获取当前分配
        List<ProdMachine> availableMachines = machinesMapper.selectAll().stream()
                .filter(machine -> machine.getWorkCenterId().equals(currentAssignment.getWorkCenter().getWorkCenterId())) // 过滤出属于当前工作中心的设备
                .collect(Collectors.toList());
        ProdMachine newMachine = availableMachines.get(rand.nextInt(availableMachines.size())); // 随机选择一个设备

        newSolution.getAssignments().put(selectedOrder, new Assignment(currentAssignment.getWorkCenter(), newMachine)); // 更新邻域解
        return newSolution;
    }

    // 接受更差解的概率
    private boolean acceptSolution(double currentEnergy, double newEnergy, double temperature) {
        if (newEnergy < currentEnergy) {
            return true; // 如果新解更好，直接接受
        }
        double probability = Math.exp((currentEnergy - newEnergy) / temperature); // 计算接受更差解的概率
        return Math.random() < probability; // 随机决定是否接受
    }

    // 保存优化后的生产计划到数据库
    private ProdProductionPlans saveOptimizedProductionPlan(SchedulingSolution solution) {
        ProdProductionPlans newPlan = new ProdProductionPlans(); // 创建新的生产计划
        newPlan.setPlanName("Optimized Plan - " + new Date()); // 设置计划名称
        newPlan.setPlanStartDate(new Date()); // 设置计划开始日期
        Calendar calendar = Calendar.getInstance(); // 获取当前日期
        calendar.add(Calendar.DAY_OF_YEAR, 30); // 假设生产计划持续 30 天
        newPlan.setPlanEndDate(calendar.getTime()); // 设置计划结束日期

        // 保存生产计划
        productionPlansMapper.insert(newPlan);

        // 生成计划详细信息并与生产订单关联
        for (Map.Entry<ProdProductionOrders, Assignment> entry : solution.getAssignments().entrySet()) {
            ProdProductionOrders order = entry.getKey(); // 获取生产订单
            Assignment assignment = entry.getValue(); // 获取分配
            // 将生产订单与工作中心、机器设备进行关联（或存储到相关表）
            // 这里假设我们创建了一个生产计划明细表来关联生产订单
        }

        return newPlan; // 返回保存的生产计划
    }

    // 用于表示生产计划解的内部类
    private static class SchedulingSolution {
        private Map<ProdProductionOrders, Assignment> assignments; // 生产订单与分配的映射

        public SchedulingSolution(Map<ProdProductionOrders, Assignment> assignments) {
            this.assignments = assignments;
        }

        public Map<ProdProductionOrders, Assignment> getAssignments() {
            return assignments;
        }

        public SchedulingSolution copy() {
            Map<ProdProductionOrders, Assignment> copiedAssignments = new HashMap<>(assignments);
            return new SchedulingSolution(copiedAssignments);
        }
    }

    // 用于表示生产计划中的分配的内部类
    private static class Assignment {
        private ProdWorkCenter workCenter; // 工作中心
        private ProdMachine machine; // 设备

        public Assignment(ProdWorkCenter workCenter, ProdMachine machine) {
            this.workCenter = workCenter;
            this.machine = machine;
        }

        public ProdWorkCenter getWorkCenter() {
            return workCenter;
        }

        public ProdMachine getMachine() { // 获取设备
            return machine;
        }
    }
}
